package com.xy.service.calculate;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xy.api.constant.LadderCostAttributeConstant;
import com.xy.api.exception.BizException;
import com.xy.api.model.dto.calculate.AttributeDTO;
import com.xy.api.model.dto.calculate.LadderCostDTO;
import com.xy.api.model.dto.calculate.LadderCostRowDTO;
import com.xy.api.model.request.calculate.lirun.temu.LadderCostRequest;
import com.xy.api.model.request.calculate.lirun.temu.SaveLadderCostRequest;
import com.xy.api.model.dto.calculate.LadderCostResultDTO;
import com.xy.api.service.calculate.ILadderCostService;
import com.xy.auth.UserContext;
import com.xy.mapper.calculate.LadderCostMapper;
import com.xy.model.po.calculate.LadderCostPO;
import com.xy.utils.CustomBeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class LadderCostServiceImpl implements ILadderCostService {

    @Autowired
    private LadderCostMapper ladderCostMapper;

    @Override
    public LadderCostResultDTO queryLadderCostList(LadderCostRequest request) {
        LadderCostResultDTO result = new LadderCostResultDTO();
        LambdaQueryWrapper<LadderCostPO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(LadderCostPO::getIsDeleted, 0)
                .eq(LadderCostPO::getBizCode, request.getBizCode());
        List<LadderCostPO> ladderCostPOS = ladderCostMapper.selectList(queryWrapper);
        List<LadderCostDTO> ladderCostList = CustomBeanUtil.copyListProperties(ladderCostPOS, LadderCostDTO::new);
        if (CollectionUtils.isEmpty(ladderCostList)) {
            return result;
        }

        Map<BigDecimal, List<LadderCostDTO>> costMap = ladderCostList.stream().collect(Collectors.groupingBy(e -> e.getCost()));
        List<LadderCostRowDTO> list = new ArrayList<>();
        for (Map.Entry<BigDecimal, List<LadderCostDTO>> entry : costMap.entrySet()) {
            Map<String, LadderCostDTO> attributeNameMap = entry.getValue().stream().collect(Collectors.toMap(e -> e.getAttributeName(), e -> e));

            LadderCostRowDTO ladderCostRow = new LadderCostRowDTO();
            ladderCostRow.setCost(entry.getKey());
            ladderCostRow.setRow(attributeNameMap);
            list.add(ladderCostRow);
        }
        list.sort(new Comparator<LadderCostRowDTO>() {
            @Override
            public int compare(LadderCostRowDTO o1, LadderCostRowDTO o2) {
                return o1.getCost().compareTo(o2.getCost());
            }
        });

        List<String> attributeNameList = ladderCostList.stream().map(e -> e.getAttributeName()).distinct().collect(Collectors.toList());
        List<AttributeDTO> attributeList = getAttributeList(attributeNameList);
        result.setAttributeList(attributeList);
        result.setList(list);
        return result;
    }

    /**
     * 构建阶梯金额的表头
     *
     * @param attributeNameList
     * @return
     */
    private List<AttributeDTO> getAttributeList(List<String> attributeNameList) {
        Assert.notEmpty(attributeNameList, "属性非空");
        List<AttributeDTO> attributeList = new ArrayList<>();
        for (String key : attributeNameList) {
            String name = LadderCostAttributeConstant.getName(key);
            AttributeDTO attributeLeft = new AttributeDTO();
            attributeLeft.setKey(key);
            attributeLeft.setName(name);
            attributeList.add(attributeLeft);
        }
        return attributeList;
    }

    private List<LadderCostPO> checkAndGetLadderCost(SaveLadderCostRequest request) {
        Assert.hasText(request.getBizCode(), "业务CODE必填");
        Assert.notEmpty(request.getList(), "区间必填");
        Long userId = UserContext.get().getId();
        String bizCode = request.getBizCode();
        List<LadderCostDTO> allLadderCostList = new ArrayList<>();
        for (LadderCostRowDTO ladderCostRow : request.getList()) {
            Map<String, LadderCostDTO> row = ladderCostRow.getRow();
            if (!CollectionUtils.isEmpty(row)) {
                allLadderCostList.addAll(row.values());
            }
        }

        Map<String, List<LadderCostDTO>> attributeMap = allLadderCostList.stream().collect(Collectors.groupingBy(e -> e.getAttributeName()));
        List<LadderCostPO> insertList = new ArrayList<>();
        for (Map.Entry<String, List<LadderCostDTO>> attributeEntry : attributeMap.entrySet()) {
            String attributeName = attributeEntry.getKey();
            List<LadderCostDTO> ladderCostList = attributeEntry.getValue();
            ladderCostList.sort(new Comparator<LadderCostDTO>() {
                @Override
                public int compare(LadderCostDTO o1, LadderCostDTO o2) {
                    if (null == o1 || null == o1.getCost()) {
                        return 0;
                    }
                    if (null == o2 || null == o2.getCost()) {
                        return 0;
                    }
                    return o1.getCost().compareTo(o2.getCost());
                }
            });
            String unit = LadderCostAttributeConstant.getUnit(attributeName);

            BigDecimal lastLeft = BigDecimal.valueOf(-1);
            for (int i = 0; i < ladderCostList.size(); i++) {
                LadderCostDTO ladderCostDTO = ladderCostList.get(i);
                if ((null == ladderCostDTO.getLeftValue() || Objects.equals(0, ladderCostDTO.getLeftValue())) &&
                        (null == ladderCostDTO.getRightValue() || Objects.equals(0, ladderCostDTO.getRightValue()))
                ) {
                    continue;
                }
                if (null != ladderCostDTO.getLeftValue() && null != ladderCostDTO.getRightValue()) {
                    if (ladderCostDTO.getLeftValue().compareTo(ladderCostDTO.getRightValue()) >= 0) {
                        throw new BizException("保存的区间范围异常");
                    }
                }
                if (null == ladderCostDTO.getCost()) {
                    throw new BizException("金额异常");
                }
                if (null != ladderCostDTO.getLeftValue() && null != lastLeft && ladderCostDTO.getLeftValue().compareTo(lastLeft) < 0) {
                    throw new BizException("区间重叠");
                }
                lastLeft = ladderCostDTO.getRightValue();

                LadderCostPO record = new LadderCostPO();
                record.setBizCode(bizCode);
                record.setAttributeName(attributeName);
                record.setLeftValue(ladderCostDTO.getLeftValue());
                record.setIncludeLeft(0);
                record.setRightValue(ladderCostDTO.getRightValue());
                record.setIncludeRight(1);
                record.setCost(ladderCostDTO.getCost());
                record.setUnit(unit);
                record.setCreateBy(userId);
                insertList.add(record);
            }
        }
        return insertList;
    }

    @Override
    public void saveLadderCost(SaveLadderCostRequest request) {
        List<LadderCostPO> insertList = checkAndGetLadderCost(request);
        Long userId = UserContext.get().getId();
        LambdaQueryWrapper<LadderCostPO> deleteWrapper = new LambdaQueryWrapper<>();
        deleteWrapper.eq(LadderCostPO::getBizCode, request.getBizCode());
        LadderCostPO deleteRecord = new LadderCostPO();
        deleteRecord.setIsDeleted(1);
        deleteRecord.setUpdateBy(userId);
        ladderCostMapper.update(deleteRecord, deleteWrapper);
        ladderCostMapper.batchInsert(insertList);
    }

    @Override
    public BigDecimal getLadderCost(String bizCode, Map<String, BigDecimal> valueMap) {
        LambdaQueryWrapper<LadderCostPO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(LadderCostPO::getIsDeleted, 0)
                .eq(LadderCostPO::getBizCode, bizCode);
        List<LadderCostPO> ladderCostPOS = ladderCostMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(ladderCostPOS)) {
            throw new BizException("阶梯价格为设置");
        }
        Map<String, List<LadderCostPO>> ladderCostMap = ladderCostPOS.stream().collect(Collectors.groupingBy(e -> e.getAttributeName()));

        BigDecimal cost = null;
        for (Map.Entry<String, List<LadderCostPO>> ladderCostEntry : ladderCostMap.entrySet()) {
            String key = ladderCostEntry.getKey();
            if (!valueMap.containsKey(key)) {
                continue;
            }
            BigDecimal value = valueMap.get(key);
            BigDecimal ladderCostCost = calculateLadderCost(ladderCostEntry.getValue(), value);
            if (cost == null) {
                cost = ladderCostCost;
            } else {
                cost = cost.max(ladderCostCost);
            }
        }
        return cost;
    }

    /**
     * 计算阶梯价格
     *
     * @param list
     * @param value
     * @return
     */
    private BigDecimal calculateLadderCost(List<LadderCostPO> list, BigDecimal value) {
        if (CollectionUtils.isEmpty(list)) {
            return BigDecimal.ZERO;
        }
        list.sort(new Comparator<LadderCostPO>() {
            @Override
            public int compare(LadderCostPO o1, LadderCostPO o2) {
                return o1.getLeftValue().compareTo(o2.getLeftValue());
            }
        });

        for (LadderCostPO ladderCostPO : list) {
            boolean inRange = true;
            if (null == ladderCostPO.getLeftValue()) {
                inRange &= true;
            } else {
                if (Objects.equals(ladderCostPO.getIncludeLeft(), 1)) {
                    inRange &= value.compareTo(ladderCostPO.getLeftValue()) >= 0;
                } else {
                    inRange &= value.compareTo(ladderCostPO.getLeftValue()) > 0;
                }
            }

            if (null == ladderCostPO.getRightValue()) {
                inRange &= true;
            } else {
                if (Objects.equals(ladderCostPO.getIncludeRight(), 1)) {
                    inRange &= value.compareTo(ladderCostPO.getRightValue()) <= 0;
                } else {
                    inRange &= value.compareTo(ladderCostPO.getRightValue()) < 0;
                }
            }
            if (inRange) {
                return ladderCostPO.getCost();
            }
        }
        return BigDecimal.ZERO;
    }
}
